package cbbx_sm.probabilistic_model;

import java.util.HashMap;
import java.util.List;

import cbbx_sm.utils.Timestamp;

/**
 * A class that implement a noisy-or predictor..
 *
 * @author Alessio Della Motta - University of California, Irvine
 *
 */
public class NoisyOrPredictor implements IPredictor{
//	private final static double CUTOFF_PROBABILITY = 0.0;
	private boolean aPriori;
	private SystemShortTermMemory memory;
	private ProbabilisticModel probModel;
	private int predictorTimeWindow;
	private List<Cluster> clusters;
	protected boolean singleCameraModel = false;
	private int timeInFuture;
	
	/**
	 * Creates a probability estimation for {@code timeInFuture} seconds in the future.
	 * 
	 * @param memory the states that were seen so far
	 * @param probModel the transition model
	 * @param clusters clusters
	 * @param aPriori use apriori probabilities when computing probability.
	 * @param timeInFuture 0 or 1 seconds in the future
	 */
	public NoisyOrPredictor(SystemShortTermMemory memory, 
							ProbabilisticModel probModel, 
							List<Cluster> clusters,
							boolean aPriori,
							int timeInFuture){
		
		this.memory = memory;
		this.probModel = probModel;
		this.aPriori = aPriori;
		
		this.predictorTimeWindow = memory.memoryCapacity();
		
		this.clusters = clusters;
		
		this.timeInFuture = timeInFuture;
	}
	 
	
	/* (non-Javadoc)
	 * @see cbbx_sm.probabilistic_model.IPredictor#makePrediction()
	 * NOTE: this prediction method is responsible of completing the picture for the CURRENT TIME
	 *       by computing the probabilities for the regions that the scheduler did not visit. 
	 */
	@Override
	public Prediction makePrediction(Timestamp currentTime) {
		Prediction prediction = new Prediction();
		HashMap<Cluster, Double> probabilities = new HashMap<Cluster, Double>();
		for (Cluster curCluster : clusters){
			double product = 1.0;

			// Computing the probabilities for the current timestamp.
			for (int t = 0; t < predictorTimeWindow; t++) {
				Timestamp pastTimestamp = Timestamp.getTimeStampInPast(currentTime, t);
				SystemState pastState = memory.getSystemState(pastTimestamp);
				
				// If I've no info about t, suppose nothing happened...
				if (pastState == null || pastState.getClusterStates().size() == 0) continue;
				
				for (Cluster c : clusters) {
					if (singleCameraModel) {
						if (curCluster.getCameraId().compareTo(c.getCameraId()) != 0){
							continue;
						}						
					}
					
					double prob = 0;
					if (pastState.getClusterStates().containsKey(c)) {
						// getting alpha(j,i)(t) for time t
						// Playing with alpha(j,i)(t+1) for probabilities for the next timestamp
						prob = probModel.getIndependentTimeCorrelationProbability(
										t + timeInFuture,
										1,
										c,
										curCluster);
					} /*else {
						prob = probModel.getIndependentTimeCorrelationProbability(
								t,
								0,
								c,
								curCluster);						
					}*/

	
					// Updating inside product
					product *= (1 - prob);
				}
			}
			// Updating the product
			if (aPriori) {
				product *= (1 - probModel.getAPrioriProbability(curCluster)); // With APriori
			}
			
			// Set probability in the hashmap
			Double prob = 1.0 - product;
			probabilities.put(curCluster, prob);
		}
		
		prediction.setProbabilities(probabilities);
		
		return prediction;
	}
	
	@Override
	public String toString(){
		return "" + this.getClass().getSimpleName() + "_" + aPriori + "_" + timeInFuture + "\t" + predictorTimeWindow;
	}
}

